package com.ccsoft.core;

import com.ccsoft.constant.Constant;
import com.ccsoft.utils.FileUtils;
import com.ccsoft.utils.FilesizeUtils;
import com.ccsoft.utils.HttpUtils;
import com.ccsoft.utils.LogUtils;

import java.io.*;
import java.net.HttpURLConnection;
import java.util.ArrayList;
import java.util.concurrent.*;

public class Downloader {

    public ScheduledExecutorService executorService = Executors.newScheduledThreadPool(1);
//    public ScheduledExecutorService mergeInfoService = Executors.newScheduledThreadPool(1);
    private ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(Constant.THREAD_NUMBER,Constant.THREAD_NUMBER,0,TimeUnit.SECONDS,new LinkedBlockingDeque<>());
    private CountDownLatch countDownLatch = new CountDownLatch(Constant.THREAD_NUMBER);

    public String download(String url){
        String fileName = HttpUtils.getFileName(url);// 从 url 中截取文件名
        String fullFileName = Constant.DEFAULT_LOCAL_PATH + fileName;// 使用默认下载目录拼接文件名获取路径文件名

        // 如果本地已经存在同名文件则通知用户
        long localFileLength = FileUtils.getFileContentLength(fullFileName);// 获取文件大小
        if( localFileLength >0 ){
            LogUtils.info("文件 {} 已下载过，本地文件的大小：{} ",fileName,FilesizeUtils.getFilesizeString(localFileLength));
        }

        HttpURLConnection httpURLConnection = null;
        DownloaderInfoThread downloaderInfoThread = null;
        try {
            httpURLConnection = HttpUtils.getHttpURLConnection(url);

            int httpFileLength = httpURLConnection.getContentLength();// 网络文件大小
            LogUtils.info("网络文件大小：{}",FilesizeUtils.getFilesizeString(httpFileLength));
            if( localFileLength >= httpFileLength ){// 文件已经下载完成则退出不再下载
                LogUtils.info("文件 [ {} ] 已经下载完成，无需重复下载！",fileName);
                return "总耗时0";
            }

            downloaderInfoThread = new DownloaderInfoThread(httpFileLength);
            LogUtils.info(downloaderInfoThread.getSplitInfo());
            // 每一秒执行一次下载信息的组织获取
            executorService.scheduleAtFixedRate(downloaderInfoThread,1,1, TimeUnit.SECONDS);

            // 切分任务执行
            ArrayList<Future<Boolean>> futureList = new ArrayList<>();
            createThreadTasks(url,futureList);// 将下载任务分割执行

            // 遍历保证所有线程都下载完毕
//            futureList.forEach( item -> {
//                try {
//                    item.get();
//                } catch (InterruptedException e) {
//                    e.printStackTrace();
//                } catch (ExecutionException e) {
//                    e.printStackTrace();
//                }
//            });

            // 使用 CountDownLatch 计数，类似上面被注释的代码
            // 等待所有线程都执行完毕后才继续向下执行
            // 尝试使用 ForkJoinPool 分治多线程
            countDownLatch.await();
            executorService.shutdown();

            // 合并文件
            mergeFile(fileName);
        } catch (IOException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            executorService.shutdown();// 立即关闭线程池

            if( httpURLConnection != null)
                httpURLConnection.disconnect();
        }

        return downloaderInfoThread.getDurationString();
    }

    // 创建多线程任务
    private void createThreadTasks(String url, ArrayList<Future<Boolean>> futureList){
        HttpURLConnection httpURLConnection = null;
        long httpFileLength = 0;
        try {
            httpURLConnection = HttpUtils.getHttpURLConnection(url);
            httpFileLength = httpURLConnection.getContentLength();// 网络文件的总大小

            long size = httpFileLength / Constant.THREAD_NUMBER;// 每个块的大小

            // 每个线程分配一个下载任务
            for (int i = 0; i < Constant.THREAD_NUMBER; i++) {
                long startPos = i * size;

                // 最后一个块的结束位置是0
                long endPos = 0;

                // 非最后块的结束位置是起始位置 + 块大小
                if( i != Constant.THREAD_NUMBER - 1 )
                    endPos = startPos + size;

                // 非第一块的起始位置要 + 1
                if( i !=0 )
                    startPos++;

                // 每个线程读取网络文件的一部分进行下载
                DownloaderTask downloaderTask = new DownloaderTask(url,startPos,endPos,i,countDownLatch);
                Future<Boolean> future = threadPoolExecutor.submit(downloaderTask);
                futureList.add(future);
            }

        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            threadPoolExecutor.shutdown();

            if( httpURLConnection != null )
                httpURLConnection.disconnect();
        }

    }

    // 合并临时文件为原始文件
    private Boolean mergeFile(String fileName){
        // 被分割的临时文件名构成的数组
        // 例如：abc.exe.tmp1、abc.exe.tmp2...
        String[] fileNames = FileUtils.getTempFilenameArray(fileName,Constant.THREAD_NUMBER);
        if( fileNames == null || fileNames.length <= 0 ) return false;

        String fullFilename = Constant.DEFAULT_LOCAL_PATH + fileName;// 带有绝对路径的完整文件名
        byte[] buffer = new byte[Constant.BYTE_SIZE];
        int len = -1;

        try {
            RandomAccessFile accessFile = new RandomAccessFile(fullFilename,"rw");
            System.out.println("\r");
            LogUtils.info("开始合并文件...");

            MergeInfoThread mergeInfoThread = new MergeInfoThread(fileName);
//            mergeInfoService.scheduleAtFixedRate(mergeInfoThread,10,10, TimeUnit.MILLISECONDS);

            for (int i = 0; i < fileNames.length; i++) {
                String fullTmpFilename = Constant.DEFAULT_LOCAL_PATH + fileNames[i];
                FileInputStream fileInputStream = new FileInputStream(fullTmpFilename);
                BufferedInputStream bufferedInputStream = new BufferedInputStream(fileInputStream);

                while(true){
                    try {
                        if (!((len = bufferedInputStream.read(buffer)) != -1)) break;
                        accessFile.write(buffer,0,len);
                    } catch (IOException e) {
                        e.printStackTrace();
                        return false;
                    }

                }

                fileInputStream.close();
                bufferedInputStream.close();
            }

            accessFile.close();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
            return false;
        } catch (IOException e) {
            e.printStackTrace();
            return false;
        } finally {
//            mergeInfoService.shutdown();
        }

        // 合并后删除文件
        deleteTempFile(fileName);
        return true;
    }

    // 删除临时文件
    private Boolean deleteTempFile(String filename){
        String[] filenames = FileUtils.getTempFilenameArray(filename,Constant.THREAD_NUMBER);
        if( filenames == null || filenames.length <=0 ) return true;

        for (int i = 0; i < filenames.length; i++) {
            String tmpFilename = Constant.DEFAULT_LOCAL_PATH + filenames[i];
            File file = new File(tmpFilename);
            file.delete();// 删除

//            LogUtils.info("临时文件{}",tmpFilename);
//            if(file.delete())
//                LogUtils.info("删除成功！");
//            else
//                LogUtils.info("删除失败！");
        }

        return true;
    }
}
